#include "Particles.h"


// Assignment 3.
//	1. F1 : toggle between ortho/perspective mode of viewing.
//	2. F2 : toggle between continuous rotation about vertical axis and stop rotation
//	3. Up and Down Arrow keys for rotating view about	horizontal axis
//	4. F3 and F4 for zoom in and zoom out
//	5. F10: reset view in case your view parameters get out of control.
/*

#include <stdlib.h>
#include <stdarg.h>		// For va_start(...)
#include <stdio.h>
#include <GL/glut.h>	// For openGL
#include <time.h>		// For time(...)

#include <math.h>		// For rand() and RAND_MAX
#include <iostream>		// For cout and cin

using namespace std;	// For cout and cin


// Initial size of graphics window on your screen.
const int WIDTH	= 600; // in pixels
const int HEIGHT = 600; 

// Increment for idle function rotation and zoom
const double RotSTEP = 0.1;
const double ALL_ROUND = 360;
const double ZoomSTEP = 0.2;
const double zoomFactor = 1.03;

// Current size of window (will change when you resize window)
int width	= WIDTH;
int height = HEIGHT;

// Mouse positions, normalized to [0,1].
double xMouse = 0.5;
double yMouse = 0.5;

// Bounds of viewing frustum, maintain uniform scaling.
double viewWindowLeft =	-40;
double viewWindowRight	= 40;
double viewWindowBottom =	-40;
double viewWindowTop	= 40;
double nearPlane =	40;
double farPlane	= 120;

// Viewing angle.
double fovy = 60;

// Variables.
double alpha = 0;	// Rotation angle about Y axis.
double beta = 0;	// Rotation angle about X axis.
double halfway = - (farPlane + nearPlane) / 2;	// half way between near and far planes
void *font = GLUT_BITMAP_9_BY_15;

int idleEnable = 0;	// Flags that set continuous rotation on/off
int projType = 0;	// Flag for proj type; ortho = 0, perspective = 1
*/


GLint flow = 0;
GLint activeFlow = 0;
GLint current = 0;
GLint decrease = 0;
GLfloat MAXLEVEL = 10.0f-0.01f;
GLfloat coneBaseRadius = 3.0f-0.01f;
GLfloat coneTopRadius = 0.0f;
GLfloat coneWaterLevel = 10.0f-0.01f;
GLfloat area = 0.0f;
PARTICLE particle[MAXPARTICLE];
GLfloat particleHeight;
GLint TempFlow;
//bool decrease = false;

void CreateParticle(GLint i)
{
	particle[i].lifetime= 500*rand()/(GLdouble)RAND_MAX;
	particle[i].decay=0.001;
	particle[i].r = 0.0;
	particle[i].g = 0.;
	particle[i].b = 1.0;
	particle[i].radius = 0.5*rand()/(GLdouble)RAND_MAX;
	particle[i].xpos= -particle[i].radius;
	particle[i].ypos = particle[i].initalHeight= 10.0;
	particle[i].zpos= -particle[i].radius;
	particle[i].xspeed = particle[i].radius/10000-0.2*rand()/(GLdouble)RAND_MAX;
	particle[i].yspeed = -particle[i].radius/2;
	particle[i].zspeed = particle[i].radius/10000-0.2*rand()/(GLdouble)RAND_MAX;
	particle[i].active = true;
	particle[i].decrease = true;
	particleHeight = particle[i].initalHeight;
}

void EvolveParticle()
{
	for(GLint i=0;i<current;i++) {	// evolve the particle parameters
		particle[i].lifetime-=particle[i].decay;
		// added by me
		particle[i].r = particle[i].g= (particle[i].initalHeight-particle[i].ypos)/10*(1/particle[i].radius);
		particle[i].xpos+=particle[i].xspeed;
		particle[i].ypos+=particle[i].yspeed;
		particle[i].zpos+=particle[i].zspeed;
		// added by me
		// ----------------------------------
		if(particle[i].xspeed == 0) 
		{
			particle[i].xspeed = 0;
		}
		else 
		{
			if(particle[i].xspeed > 0) 
			{
				particle[i].xspeed -= 0.005;
				if(particle[i].xspeed < 0) 
				{
					particle[i].xspeed = 0;
				}
			}
			else 
			{
				particle[i].xspeed += 0.005;
				if(particle[i].xspeed > 0) 
				{
					particle[i].xspeed = 0;
				}
			}
		}
		particle[i].yspeed-=0.0007;	// except this line.
		particle[i].radius-=0.0025;
		if(particle[i].radius < 0.1) 
		{
			particle[i].radius = 0.1;
		}
		if(particle[i].zspeed == 0) 
		{
			particle[i].zspeed = 0;
		}
		else 
		{
			if(particle[i].zspeed > 0) 
			{
				particle[i].zspeed -= 0.005;
				if(particle[i].zspeed < 0) 
				{
					particle[i].zspeed = 0;
				}
			}
			else 
			{
				particle[i].zspeed += 0.005;
				if(particle[i].zspeed > 0) 
				{
					particle[i].zspeed = 0;
				}
			}
		}
	// ----------------------------------
	}
}

// **************************************************************************

/*void resetViewParameters() {
	// Resets 3D synthetic camera parameters to default values
	viewWindowLeft = -40;
	viewWindowRight	= 40;
	viewWindowBottom = -40;
	viewWindowTop = 40;
	nearPlane =	40;
	farPlane = 120;
	fovy = 60;
	alpha = 0.0;
	beta = 0.0;
	projType = 0;
	idleEnable = 0;
}
*/

/*void setView() {
	// Must set it up in Projection Matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Rotation about X from UP-DOWN Arrow key
	glRotatef(beta, 1, 0, 0);


	// Rotation about Y from idle function.
	glRotatef(alpha, 0, 1, 0);

	if(projType) {
		gluPerspective(fovy, (GLfloat) 1.0, nearPlane, farPlane);
	}
	else {
		glOrtho
		(
			viewWindowLeft,
			viewWindowRight,
			viewWindowBottom,
			viewWindowTop,
			nearPlane,
			farPlane
		);
	}
}

void zoom(unsigned char direction) {
	// For zoom in reduces viewWindow size and for zoom out increases it.

	if (direction == '+') {
		viewWindowLeft += ZoomSTEP; viewWindowRight -= ZoomSTEP;
		viewWindowBottom += ZoomSTEP; viewWindowTop -= ZoomSTEP;
		fovy = fovy / zoomFactor;
	}
	else if (direction == '-') {
		viewWindowLeft -= ZoomSTEP; viewWindowRight += ZoomSTEP;
		viewWindowBottom -= ZoomSTEP; viewWindowTop += ZoomSTEP;
		fovy = fovy * zoomFactor;
	}
}
*/
/*void drawCylinder(GLdouble baseRadius, GLdouble topRadius, GLdouble height) {

	// Glut provides functions for cube and sphere, but not for cylinder, cone.
	// Hence this glu function has to be called.

	GLUquadricObj* cyl = NULL;
	cyl = gluNewQuadric();
	gluQuadricDrawStyle(cyl, GLU_LINE);

	glPushMatrix();

		glTranslated(0, height, 0);
		glRotatef(90, 1, 0, 0);
		gluCylinder(cyl, baseRadius, topRadius, height, 6, 4);
		gluDeleteQuadric(cyl);

	glPopMatrix();
}
*/
void InitializeParticle() 
{
	for(GLint i = 0; i < MAXPARTICLE;i++) {	// initialize the particle parameters
		CreateParticle(i);
		particle[i].lifetime = 0.0;
		particle[i].ypos = 0.0;
		particle[i].active = false;		// set all particles inactive
	}
}

void DrawSphere(GLdouble Radius)
{
	glPushMatrix();
		glutSolidSphere(Radius, 20, 20);
	glPopMatrix();
}

void DrawScene()
{
	GLfloat difference;
	if(activeFlow) 
	{
		difference = coneWaterLevel - particleHeight + 8;
		TempFlow = difference * 100 / (MAXLEVEL + 8 - particleHeight);
		if(TempFlow < 1) {
			flow = 0;
		}
		else if(flow < TempFlow) {
			flow += 10;
		}
		else {
			flow = TempFlow;
		}
		if(current < flow) 
		{
			current = flow;
		}
	}
	
	for (GLint i = 0; i< current;i++) 
	{
		if(particle[i].ypos<0.0-particle[i].radius) 
		{
			particle[i].lifetime=0.0;
			if(current > flow) 
			{
				current--;
			}
		}
		if((particle[i].active) && (particle[i].lifetime>0.0))
		{
			if(particle[i].decrease) {
				difference = MAXLEVEL - coneWaterLevel;
				if(area <= 0) {
					area = coneTopRadius * coneTopRadius * 3.14159265;
				}
				area -= particle[i].radius;
				if(area < 0) {
					coneWaterLevel -= particle[i].radius/5;
					area = 0;
				}
				if(coneWaterLevel <= 0) {
					coneWaterLevel = 0;
					flow = 0;
				}
				coneTopRadius = difference*coneBaseRadius/MAXLEVEL;
				particle[i].decrease = false;
			}
			glColor3f(particle[i].r,particle[i].g,particle[i].b);
			glPushMatrix();
				glTranslated(particle[i].xpos, particle[i].ypos, particle[i].zpos);
				DrawSphere(particle[i].radius);
			glPopMatrix();
		}
		else 
		{
			if(current == flow && coneWaterLevel > 0 && coneWaterLevel > particle[i].initalHeight - 8) {
				CreateParticle(i);
			}
			else {
				for(int j=i; j < current+1; j++) {
					particle[j] = particle[j+1];
				}
				i--;
			}
			if(current <= 0) {
				activeFlow = 0;
			}
		}
	}

	//EvolveParticle();
	//glutPostRedisplay();
}


// Output using glutStrokeCharacter.
/*void output(GLfloat x, GLfloat y, char *format,...) {
	va_list args;
	char buffer[200], *p;

	va_start(args, format);
	vsprintf(buffer, format, args);
	va_end(args);
	glPushMatrix();
	glTranslatef(x, y, 0);
	for (p = buffer; *p; p++)
		glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
	glPopMatrix();
}


// Output using glutBitmapCharacter.
/*void output(int x, int y, char *string) {
	int len, i;

	//glRasterPos2f(x, y);
	len = (int) strlen(string);
	for (i = 0; i < len; i++) {
		glutBitmapCharacter(font, string[i]);
	}
}
*/

// This function is called to display the scene.
void DisplayParticles ()
{
	//setView();

	//glClear(GL_COLOR_BUFFER_BIT);

	// Set modelling mode
	//glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();
	//	Position so that container box is placed within the near and far planes.
	//	This will prevent them from getting clipped completely.
	//glTranslated(0, 0, halfway);

	// Draw model axes in centre of room.
/*	glBegin(GL_LINES);
		// X axis
		glColor3f(1, 0, 0);
		glVertex3f(0, 0, 0);
		glVertex3f(10, 0, 0);
		// Y axis
		glColor3f(0, 1, 0);
		glVertex3f(0, 0, 0);
		glVertex3f(0, 10, 0);
		// Z axis
		glColor3f(0, 0, 1);
		glVertex3f(0, 0, 0);
		glVertex3f(0, 0, 10);
	glEnd();
*/
	// Draw the scene in double buffer.
	DrawScene();
	//glutSwapBuffers();
}

// This function is called when there is nothing else to do.
/*void idle () {
	if (idleEnable) {
		alpha += RotSTEP;
		if (alpha > ALL_ROUND) {
			alpha -= ALL_ROUND;
		}
		// Display normalized coordinates in title bar.
		glutPostRedisplay();
	}
}*/

/*void mouseMovement (int mx, int my) {
	// Normalize mouse coordinates.
	xMouse = double(mx) / double(width);
	yMouse = 1 - double(my) / double(height);

	// Redisplay image.
	glutPostRedisplay();
}

// Respond to window resizing, preserving proportions.
// Parameters give new window size in pixels.
void reshapeMainWindow (int newWidth, int newHeight) {
	width = newWidth;
	height = newHeight;
	glViewport(0, 0, width, height);

}

// Display help.
void help() {
	cout <<
	"Assignment 1.\n\n"
	"Keys have the following effect:\n"
	"	a/A	to set the no force.\n"
	"	b/B	to set the gravity force.\n"
	"	c/C	to set the drag force.\n"
	"	d/D	to set the kinetic force.\n"
	"	e/E	to set the all forces.\n"
	"\n"
	"	UP/DOWN ARROW	key	rotate 1 degree up/down.\n"
	"	LEFT/RIGHT ARROW	key	rotate 1 degree left/right.\n"
	"\n"
	"	x/X	subtract/add 0.5 to X-axis direction vector.\n"
	"	y/Y	subtract/add 0.5 to Y-axis direction vector.\n"
	"	z/Z	subtract/add 0.5 to Z-axis direction vector.\n"
	"	o/O	reduce/increase 10 of the box size.\n"
	"	p/P	reduce/increase 1 of the ball proportion.\n"
	"\n"
	"	F10	reset all data."
	<< endl;
}


// Respond to graphic character keys.
// Parameters give key code and mouse coordinates.
void graphicKeys (unsigned char key, int x, int y) {
	switch (key) {
	case 27:
		exit(0);
	}
}
*/
// Respond to function keys.
// Parameters give key code and mouse coordinates.
void FunctionKeys (int key, int x, int y) 
{
	switch (key) 
	{
		case GLUT_KEY_PAGE_UP:
			/*flow += 10;
			if (flow > MAXPARTICLE)
				flow = MAXPARTICLE;
			for(int i = current; i < flow; i++) 
			{
				CreateParticle(i);
			}*/
			activeFlow = 1;
			break;
		case GLUT_KEY_PAGE_DOWN:
			/*flow -= 10;
			if (flow < 0)
				flow = 0;
				*/
			activeFlow = 0;
			break;
	}
}

GLfloat GetConeWaterLevel() {
	return coneWaterLevel;
}

GLfloat GetConeTopRadius() {
	return coneTopRadius;
}
	/*case GLUT_KEY_F10:
		//resetViewParameters();
		break;
	case GLUT_KEY_F1:
		//projType = 1 - projType;
		break;
	case GLUT_KEY_F2:
		//idleEnable = 1 - idleEnable;
		break;
	case GLUT_KEY_F3:
		zoom('+');
		break;
	case GLUT_KEY_F4:
		zoom('-');
		break;
	case GLUT_KEY_UP:
		flow += 10;
		if (beta > ALL_ROUND)
			beta -= ALL_ROUND;
		break;
	case GLUT_KEY_DOWN:
		beta -= 10*RotSTEP;
		if (beta < -ALL_ROUND)
			beta += ALL_ROUND;
		break;
	case GLUT_KEY_LEFT:
		alpha += 10*RotSTEP;
		if (alpha > ALL_ROUND)
			alpha = 0;
		break;
	case GLUT_KEY_RIGHT:
		alpha -= 10*RotSTEP;
		if (alpha < -ALL_ROUND)
			alpha = 0;
		break;*/

	//}

	//glutPostRedisplay();
//}

/*void main (int argc, char **argv) {

	// GLUT initialization. Enable double buffer mode
	init();
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(width, height);
	glutCreateWindow("Assignment 1");
	// Register call backs for standard tasks such as:
	// Display, windowresize, keypress, functionkey press, mouse movement.
	glutDisplayFunc(display);
	glutReshapeFunc(reshapeMainWindow);
	glutKeyboardFunc(graphicKeys);
	glutSpecialFunc(functionKeys);	// Press ESC to exit.
	glutMotionFunc(mouseMovement);
	glutIdleFunc(idle);

	// Display help.
	 help();

	// Enter GLUT loop.
	glutMainLoop();
}*/

